//
//  TPLabel.m
//  Pods
//
//  Created by caixun on 15/11/4.
//
//

#import "TPLabel.h"
#import <CoreText/CoreText.h>


#define TP_Label_Target @"kTPLabelTarget" /// 添加的点击事件target对应的key值
#define TP_Label_SelAction @"kTPLabelAction" /// 添加的点击事件方法名对应的key值
#define TP_Label_Range @"kTPLabelRange" /// 添加的点击事件文字范围对应的key值

#define TP_Label_NormalTextColor @"kTPLabelNormalTextColor" /// 普通状态下文字颜色对应的key值
#define TP_Label_HightLightTextColor @"kTPLabelHightLightTextColor" /// 高亮状态下文字颜色对应的key值
#define TP_Label_NormalBgColor @"kTPLabelNormalBgColor" /// 普通状态下背景颜色对应的key值
#define TP_Label_HighLightBgColor @"kTPLabelHighLightBgColor" /// 高亮状态下背景颜色对应的key值


@interface TPLabel ()
{
    /// 属性字符串
    NSMutableAttributedString *matbstr_;
}

/// 存储添加的target、action信息的数组
@property (nonatomic, strong) NSMutableArray *maryTargets;


@end


@implementation TPLabel


#pragma mark - *********************************** View Lifecycle **********************************

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        /// 设置背景为透明色
        self.backgroundColor = [UIColor clearColor];
        /// 默认文字颜色为黑色
        self.textColor = [UIColor blackColor];
        
        /// 初始化变量
        [self initVariable];
    }
    
    return self;
}

- (void)initVariable
{
    _maryTargets = [[NSMutableArray alloc] init];
}

- (void)dealloc
{
    
}


#pragma mark - ********************************** 初始化方法 工厂方法 *********************************

/// 设置frame
+ (TPLabel *)getWithFrame:(CGRect)frame
{
    TPLabel *lab = [[TPLabel alloc] initWithFrame:frame];
    
    return lab;
}

/// 设置frame、文字颜色、字体
+ (TPLabel *)getWithFrame:(CGRect)frame
                      text:(NSString *)strText
                 textColor:(UIColor *)colorText
                  textFont:(UIFont *)font
{
    TPLabel *lab = [[TPLabel alloc] initWithFrame:frame
                                               text:strText
                                          textColor:colorText
                                           textFont:font];
    return lab;
}

/// 设置frame、文字颜色、字体、对齐方式
+ (TPLabel *)getWithFrame:(CGRect)frame
                      text:(NSString *)strText
                 textColor:(UIColor *)colorText
                  textFont:(UIFont *)font
             textAlignment:(NSTextAlignment)textAlignment
{
    TPLabel *lab = [[TPLabel alloc] initWithFrame:frame
                                               text:strText
                                          textColor:colorText
                                           textFont:font
                                      textAlignment:textAlignment];
    return lab;
}


#pragma mark - ********************************** 初始化方法 实例方法 *********************************

/// 设置frame、文字颜色、字体
- (id)initWithFrame:(CGRect)frame
               text:(NSString *)strText
          textColor:(UIColor *)colorText
           textFont:(UIFont *)font
{
    /// 默认为居左显示
    return [self initWithFrame:frame
                          text:strText
                     textColor:colorText
                      textFont:font
                 textAlignment:NSTextAlignmentLeft];
}

/// 设置frame、文字颜色、字体、对齐方式
- (id)initWithFrame:(CGRect)frame
               text:(NSString *)strText
          textColor:(UIColor *)colorText
           textFont:(UIFont *)font
      textAlignment:(NSTextAlignment)textAlignment
{
    if (self = [self initWithFrame:frame])
    {
        self.textColor = colorText;
        self.font = font;
        self.textAlignment = textAlignment;
        self.text = strText;
    }
    
    return self;
}


#pragma mark - *********************************** 属性设置 实例方法 **********************************

/// 重写setText:方法
- (void)setText:(NSString *)text
{
    [super setText:text];
    
    /// 初始化属性字符串
    [self initAttributeString];
}

/// 给某一范围内的文字设置颜色和字体
- (void)setTextColor:(UIColor *)colorText textFont:(UIFont *)font range:(NSRange)range
{
    if (!matbstr_) /// 为空时进行初始化
    {
        [self initAttributeString];
    }
    
    [matbstr_ addAttributes:@{NSForegroundColorAttributeName:colorText,
                              NSFontAttributeName:font}
                      range:[self protectRange:range]];
    self.attributedText = matbstr_;
}

/// 给某一范围内的文字设置背景色，文字颜色（处理点击效果时可能用到）
- (void)setBgColor:(UIColor *)colorBg textColor:(UIColor *)colorText range:(NSRange)range
{
    if (!matbstr_) /// 为空时进行初始化
    {
        [self initAttributeString];
    }
    
    /// 做空值判断，为空时不进行设置
    if (colorText)
    {
        [matbstr_ addAttribute:NSForegroundColorAttributeName value:colorText range:range];
    }
    
    if (colorBg)
    {
        [matbstr_ addAttribute:NSBackgroundColorAttributeName value:colorBg range:range];
    }
    
    self.attributedText = matbstr_;
}

/// 设置label中所有文字的行间距
- (void)setLineSpace:(CGFloat)fltLineSpace
{
    [self setLineSpace:fltLineSpace range:NSMakeRange(0, self.text.length)];
}

/// 设置label中某个范围内文字的行间距，暂不对外暴露
- (void)setLineSpace:(CGFloat)fltLineSpace range:(NSRange)range
{
    if (!matbstr_) /// 为空时进行初始化
    {
        [self initAttributeString];
    }
    
    /// 枚举遍历段落格式属性，重新设置lineSpacing
    [matbstr_ enumerateAttribute:(NSString *)kCTParagraphStyleAttributeName
                         inRange:NSMakeRange(0, matbstr_.length)
                         options:0
                      usingBlock:^(id value, NSRange range, BOOL *stop)
     {
         NSMutableParagraphStyle *paragraphStyle;
         paragraphStyle = value ? ([value mutableCopy]) : ([[NSMutableParagraphStyle alloc] init]);
         paragraphStyle.lineSpacing = fltLineSpace;
         paragraphStyle.lineBreakMode = self.lineBreakMode;
         paragraphStyle.alignment = self.textAlignment;
         [matbstr_ removeAttribute:(NSString *)kCTParagraphStyleAttributeName range:range];
         [matbstr_ addAttribute:(NSString *)kCTParagraphStyleAttributeName
                          value:paragraphStyle
                          range:range];
     }];
    
    self.attributedText = matbstr_;
}

/* 给某一范围内的文字设置点击事件、普通颜色、高亮颜色、普通背景色、高亮背景色，高亮颜色可以不传，
 普通颜色不传时使用默认颜色（文本为黑色，背景为透明色）*/
- (void)addTarget:(id)target action:(SEL)selAction
  normalTextColor:(UIColor *)colorNormalText highLightTextColor:(UIColor *)colorHighLightText
    normalBgColor:(UIColor *)colorNormalBg highLightBgColor:(UIColor *)colorHighLightBg
            range:(NSRange)range
{
    if (!matbstr_) /// 为空时进行初始化
    {
        [self initAttributeString];
    }
    
    if (!_maryTargets)
    {
        _maryTargets = [[NSMutableArray alloc] init];
    }
    
    NSMutableDictionary *mdictTarget = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                                        target,TP_Label_Target,
                                        NSStringFromSelector(selAction),TP_Label_SelAction,
                                        NSStringFromRange([self protectRange:range]),TP_Label_Range, nil];
    
    if (!colorNormalText) /// 对相应普通文字颜色为空时使用当前文字颜色
    {
        colorNormalText = self.textColor;
    }
    [mdictTarget setObject:colorNormalText forKey:TP_Label_NormalTextColor];
    
    if (colorHighLightText)
    {
        [mdictTarget setObject:colorHighLightText forKey:TP_Label_HightLightTextColor];
    }
    
    if (!colorNormalBg) /// 对相应普通背景颜色为空时使用当前背景颜色
    {
        colorNormalBg = self.backgroundColor;
    }
    [mdictTarget setObject:colorNormalBg forKey:TP_Label_NormalBgColor];
    
    if (colorHighLightBg)
    {
        [mdictTarget setObject:colorHighLightBg forKey:TP_Label_HighLightBgColor];
    }
    
    /// 保存target、action、range等相关信息到数组中
    [_maryTargets addObject:mdictTarget];
}

/// 给range加保护，超出范围时，改变range的长度
- (NSRange)protectRange:(NSRange)range
{
    if (range.location >= [matbstr_ length]) /// 位置超出字符串的长度范围
    {
#ifdef DEBUG /// 测试模式下，打印错误信息，不做处理
        NSLog(@"出错啦，location超出字符串范围啦，快去检查一下");
        
        return range;
        
#else /// 正式环境，改变location
        range.location = [matbstr_ length] - 1;
#endif
    }
    
    if (range.location + range.length > [matbstr_ length]) /// 位置和长度相加长度超出字符串范围
    {
#ifdef DEBUG /// 测试模式下，打印错误信息，不做处理
        NSLog(@"出错啦，length超出字符串范围啦，快去检查一下");
        
        return range;
        
#else /// 正式环境，改变长度
        range.length = [matbstr_ length] - range.location;
#endif
    }
    
    return range;
}

- (void)initAttributeString
{
    matbstr_ = [[NSMutableAttributedString alloc] initWithString:(self.text) ? (self.text) : @""
                                                      attributes:@{NSFontAttributeName:self.font}];
}


#pragma mark - ************************************* Functions *************************************

/// touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    /// 获取点击在label中的位置
    CGPoint point = [touch locationInView:self];
    /// 获取在点击文字的位置
    CFIndex index = [self characterIndexAtPoint:point];
    
    if (index == NSNotFound)
    {
        return;
    }
    
    for (NSInteger i = 0; i < _maryTargets.count; i++)
    {
        NSDictionary *dictTarget = _maryTargets[i];
        /// 取出文字对应的范围
        NSRange range = NSRangeFromString(dictTarget[TP_Label_Range]);
        /// 判断点击的索引是否在该范围内
        if (index >= range.location && index <= range.location + range.length)
        {
            /// 查找成功后，改变颜色
            [self setBgColor:dictTarget[TP_Label_HighLightBgColor]
                   textColor:dictTarget[TP_Label_HightLightTextColor]
                       range:range];
            break;
        }
    }
}

/// touchesEnded
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    /// 获取点击在label中的位置
    CGPoint point = [touch locationInView:self];
    /// 获取在点击文字的位置
    CFIndex index = [self characterIndexAtPoint:point];
    
    if (index == NSNotFound)
    {
        return;
    }
    
    for (NSInteger i = 0; i < _maryTargets.count; i++)
    {
        NSDictionary *dictTarget = _maryTargets[i];
        /// 取出文字对应的范围
        NSRange range = NSRangeFromString(dictTarget[TP_Label_Range]);
        /// 判断点击的索引是否在该范围内
        if ((index >= range.location) && (index <= range.location + range.length))
        {
            /// 查找成功后，恢复正常颜色
            [self setBgColor:dictTarget[TP_Label_NormalBgColor]
                   textColor:dictTarget[TP_Label_NormalTextColor]
                       range:range];
            
            id idTarget = dictTarget[TP_Label_Target];
            SEL selAction = NSSelectorFromString(dictTarget[TP_Label_SelAction]);
            if (idTarget)
            {
                /// 执行相应的事件
                [idTarget performSelector:selAction withObject:self afterDelay:0.0];
            }
            
            break;
        }
    }
    
    //    if (_delegate && [_delegate respondsToSelector:@selector(gmdOp:didEndTapCharacterAtIndex:)])
    //    {
    //        [_delegate gmdOp:self didEndTapCharacterAtIndex:index];
    //    }
}

/// 获取点击的文字所在的索引
- (CFIndex)characterIndexAtPoint:(CGPoint)point
{
    NSMutableAttributedString *optimizedAttributedText = [self.attributedText mutableCopy];
    
    [optimizedAttributedText enumerateAttribute:(NSString *)kCTParagraphStyleAttributeName
                                        inRange:NSMakeRange(0, optimizedAttributedText.length)
                                        options:0
                                     usingBlock:^(id value, NSRange range, BOOL *stop)
     {
         NSMutableParagraphStyle *paragraphStyle;
         paragraphStyle = value ? ([value mutableCopy]) : ([[NSMutableParagraphStyle alloc] init]);
         
         /// 将kCTLineBreakByTruncatingTail省略模式改为kCTLineBreakByWordWrapping
         if ([paragraphStyle lineBreakMode] == kCTLineBreakByTruncatingTail)
         {
             [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
         }
         
         [optimizedAttributedText removeAttribute:(NSString *)kCTParagraphStyleAttributeName range:range];
         [optimizedAttributedText addAttribute:(NSString *)kCTParagraphStyleAttributeName
                                         value:paragraphStyle
                                         range:range];
     }];
    
    /// 不在label范围内，返回NSNotFound
    if (!CGRectContainsPoint(self.bounds, point))
    {
        return NSNotFound;
    }
    
    CGRect rectText = [self textRect];
    
    /// 不在文字范围内，返回NSNotFound
    if (!CGRectContainsPoint(rectText, point))
    {
        return NSNotFound;
    }
    
    /// 翻转坐标，这个地方我理解的也不透彻
    point = CGPointMake(point.x - rectText.origin.x, point.y - rectText.origin.y);
    point = CGPointMake(point.x, rectText.size.height - point.y);
    
    CTFramesetterRef cfframeset = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)optimizedAttributedText);
    
    CGMutablePathRef cfmpath = CGPathCreateMutable();
    CGPathAddRect(cfmpath, NULL, rectText);
    
    CTFrameRef cfframe = CTFramesetterCreateFrame(cfframeset, CFRangeMake(0, [optimizedAttributedText length]), cfmpath, NULL);
    
    if (cfframe == NULL)
    {
        CFRelease(cfmpath);
        return NSNotFound;
    }
    
    CFArrayRef cfaryLines = CTFrameGetLines(cfframe);
    
    /// 设置的行数大于0时，取设置的行数和实际行数的最小值，设置的行数等于0时，取实际的行数
    NSInteger intNumOfLines = (self.numberOfLines > 0)
    ? (MIN(self.numberOfLines, CFArrayGetCount(cfaryLines)))
    : (CFArrayGetCount(cfaryLines));
    
    /// 实际行数为0，返回NSNotFound
    if (intNumOfLines == 0)
    {
        CFRelease(cfframe);
        CFRelease(cfmpath);
        
        return NSNotFound;
    }
    
    NSUInteger uintIndex = NSNotFound;
    
    CGPoint pointLineOrigins[intNumOfLines];
    CTFrameGetLineOrigins(cfframe, CFRangeMake(0, intNumOfLines), pointLineOrigins);
    
    for (CFIndex lineIndex = 0; lineIndex < intNumOfLines; lineIndex++)
    {
        /// 该行的起点坐标
        CGPoint pointLineOrigin = pointLineOrigins[lineIndex];
        /// 该行的CTLineRef对象
        CTLineRef cfline = CFArrayGetValueAtIndex(cfaryLines, lineIndex);
        
        /// 获取该行的bounds信息
        CGFloat fltAscent, fltDescent, fltLeading, fltWidth;
        fltWidth = CTLineGetTypographicBounds(cfline, &fltAscent, &fltDescent, &fltLeading);
        /// 该行的最小y坐标值
        CGFloat fltMinY = floor(pointLineOrigin.y - fltDescent);
        /// 该行的最大y坐标值
        CGFloat fltMaxY = ceil(pointLineOrigin.y + fltAscent);
        
        /// 判断位置是否超过了该行
        if (point.y > fltMaxY)
        {
            break;
        }
        
        if (point.y >= fltMinY) /// 垂直位置在该行范围内
        {
            /// 水平位置在该行范围内
            if (point.x >= pointLineOrigin.x && point.x <= pointLineOrigin.x + fltWidth)
            {
                /// 翻转坐标
                CGPoint pointRelative = CGPointMake(point.x - pointLineOrigin.x, point.y - pointLineOrigin.y);
                uintIndex = CTLineGetStringIndexForPosition(cfline, pointRelative);
                
                break;
            }
        }
    }
    
    CFRelease(cfframe);
    CFRelease(cfmpath);
    
    return uintIndex;
}

/// 获取文字的显示范围
- (CGRect)textRect
{
    /// 获取文本绘制的rect，然后根据对齐方式手动调整坐标值
    CGRect rectText = [self textRectForBounds:self.bounds limitedToNumberOfLines:self.numberOfLines];
    
    /// 计算文字起始y坐标
    rectText.origin.y = (self.bounds.size.height - rectText.size.height) / 2;
    
    /// 根据文字的对齐方式，计算文字起始x坐标
    if (self.textAlignment == NSTextAlignmentCenter) /// 居中显示
    {
        rectText.origin.x = (self.bounds.size.width - rectText.size.width) / 2;
    }
    else if (self.textAlignment == NSTextAlignmentRight) /// 居右显示
    {
        rectText.origin.x = self.bounds.size.width - rectText.size.width;
    }
    
    return rectText;
}


@end
